#include "DateTime.h"
#include "math.h"
#include <ostream>
using namespace std;

int DateTime::comparar(Comparable *c)
{
  c=new DateTime;
  DateTime *right;
  right=dynamic_cast<DateTime*>(c);
  bool menor=false;
	bool igual=false;
	//es mayor hasta que se demuestre lo contrario
	if (anio<right->anio) menor=true;//si el año es menor -> es menor	
	if ((anio==right->anio)&&(mes<right->mes)) menor=true;//si el año es el mismo pero el mes es menor -> es menor
	if (((anio==right->anio)&&(mes==right->mes))&&(dia<right->dia)) menor=true;
	if ((((anio==right->anio)&&(mes==right->mes))&&(dia==right->dia))&&(hora<right->hora)) menor=true;
	if (((((anio==right->anio)&&(mes==right->mes))&&(dia==right->dia))&&(hora==right->hora))&(min<right->min)) menor=true;
	if (((((anio==right->anio)&&(mes==right->mes))&&(dia==right->dia))&&(hora==right->hora))&(min==right->min)) igual=true;
	
	if (menor)
		return (-1);
	else 
		if (igual)
			return (0);
		else 
			return (1);
}
DateTime::DateTime()
	{
		time_t rawtime;
		struct tm *fecha_actual;
		time (&rawtime);
		fecha_actual=localtime (&rawtime);
		this->anio=fecha_actual->tm_year+1900;
		this->mes=fecha_actual->tm_mon+1;
		this->dia=fecha_actual->tm_mday;
		this->hora=fecha_actual->tm_hour;
		this->min=fecha_actual->tm_min;
	};
	
DateTime :: DateTime(int yyyy, int mm, int dd, int hr, int min)
	{
		this -> anio = yyyy;
		this -> mes = mm;
		this -> dia = dd;
		this -> hora = hr;
		this -> min = min;
	};

void DateTime::operator = (DateTime FechaDada)
{
		anio = FechaDada.anio;
		mes = FechaDada.mes;
		dia = FechaDada.dia;
		hora = FechaDada.hora;
		min = FechaDada.min;
};	
			
bool DateTime :: operator == (DateTime right){
    if (this->anio==right.anio && this->mes==right.mes && this->dia==right.dia && this->hora==right.hora && this->min==right.min)
        return true;
    else
        return false;
};

bool DateTime :: operator != (DateTime right){
    if (this->anio==right.anio && this->mes==right.mes && this->dia==right.dia && this->hora==right.hora && this->min==right.min)
        return false;
    else
        return true;
};

bool DateTime :: operator < (DateTime right){
    if (this->anio<right.anio)
        return true;
    else if (this->anio>right.anio)
        return false;
    else
        if (this->mes<right.mes)
            return true;
        else if (this->mes>right.mes)
            return false;
        else
            if (this->dia<right.dia)
                return true;
            else if (this->dia>right.dia)
                return false;
            else
                if (this->hora<right.hora)
                    return true;
                else if (this->hora>right.hora)
                    return false;
                else
                    if (this->min<right.min)
                        return true;
                    else
                        return false;

}

bool DateTime :: operator <= (DateTime right){
    if (this->anio<right.anio)
        return true;
    else if (this->anio>right.anio)
        return false;
    else
        if (this->mes<right.mes)
            return true;
        else if (this->mes>right.mes)
            return false;
        else
            if (this->dia<right.dia)
                return true;
            else if (this->dia>right.dia)
                return false;
            else
                if (this->hora<right.hora)
                    return true;
                else if (this->hora>right.hora)
                    return false;
                else
                    if (this->min<=right.min)
                        return true;
                    else
                        return false;

}

bool DateTime :: operator > (DateTime right){
    if (this->anio<right.anio)
        return false;
    else if (this->anio>right.anio)
        return true;
    else
        if (this->mes<right.mes)
            return false;
        else if (this->mes>right.mes)
            return true;
        else
            if (this->dia<right.dia)
                return false;
            else if (this->dia>right.dia)
                return true;
            else
                if (this->hora<right.hora)
                    return false;
                else if (this->hora>right.hora)
                    return true;
                else
                    if (this->min>right.min)
                        return true;
                    else
                        return false;
}

bool DateTime :: operator >= (DateTime right){
    if (this->anio<right.anio)
        return false;
    else if (this->anio>right.anio)
        return true;
    else
        if (this->mes<right.mes)
            return false;
        else if (this->mes>right.mes)
            return true;
        else
            if (this->dia<right.dia)
                return false;
            else if (this->dia>right.dia)
                return true;
            else
                if (this->hora<right.hora)
                    return false;
                else if (this->hora>right.hora)
                    return true;
                else
                    if (this->min<right.min)
                        return false;
                    else
                        return true;
}

ostream& operator << (ostream &o, DateTime t){
	o<<t.anio<<'/';
	t.mes<10? o<<'0'<<t.mes : o<<t.mes;
	o<<'/';
	t.dia<10? o<<'0'<<t.dia : o<<t.dia;
	o<<' ';
	t.hora<10? o<<'0'<<t.hora : o<<t.hora;
	o<<':';
	t.min<10? o<<'0'<<t.min : o<<t.min;
	return o;
}

DateTime DateTime::operator+ (const double& d) //Suma a una fecha una cantidad d de dias, devuelve una fecha
    {
        double fraccionDia = (d - floor (d))* 24; //se obtiene un numero correspondiente a la fraccion de d�as (traducido a horas) a sumar
        double minutos = (fraccionDia - floor (fraccionDia))*60; //se obtiene un numero correspondiente a la fraccion de horas (minutos) a sumar
        int carryMin_a_Hrs = floor((this -> min + minutos)/60);
        int new_min = floor (fmod(this-> min + minutos, 60)); //se guarda una variable con el nuevo valor de minutos
        int carryHrs_a_Dias = floor((this->hora + floor(fraccionDia) + carryMin_a_Hrs)/24);
        int new_hora = floor (fmod(this->hora + floor(fraccionDia) + carryMin_a_Hrs, 24));//se guarda una var con el nuevo valor de hora
        
       
        int carryDias_a_Mes = floor((this->dia + fmod(fmod(d,360),30) + carryHrs_a_Dias)/30);
        int new_dia = floor(fmod((fmod(fmod(d,360),30) + carryHrs_a_Dias + this->dia),30));//se guarda una var con el nuevo valor de dia
   
        int carryMes_a_Anio = floor((this->mes + floor(fmod(d,360)/30) + carryDias_a_Mes)/12);
        int new_mes = floor (fmod(fmod(d,360)/30 + carryDias_a_Mes + this-> mes,12)); //se guarda una var con el nuevo valor de mes
        int new_anio = this-> anio + carryMes_a_Anio + floor(d/360);//se guarda una var con el nuevo valor de anio

        if (new_dia == 0)
            new_dia = 30;

        if (new_mes == 0)
            new_mes = 12;

        DateTime sum(new_anio, new_mes, new_dia, new_hora, new_min);
        return sum;
    }

DateTime DateTime::operator- (const double& d)  //Resta a una fecha una cantidad d de dias, devuelve una fecha
    {
        
        double fraccionDia = (d - floor (d))* 24; //se obtiene un numero correspondiente a la fraccion de d�as (traducido a horas) a sumar
        double minutos = (fraccionDia - floor (fraccionDia))*60; //se obtiene un numero correspondiente a la fraccion de horas (minutos) a sumar

        int new_min = 0;
        int uncarryMin_a_Hrs = 0;
        if (minutos <= this-> min)
            new_min = floor (this-> min - minutos);
        else
            {
                new_min = floor(fmod(this->min - minutos,60));
                uncarryMin_a_Hrs = 1;
            }

        int new_hora = 0;
        int uncarryHrs_a_Dia = 0;
        if (fraccionDia < this-> hora)
            new_hora = this-> hora - floor(fraccionDia) - uncarryMin_a_Hrs;
        else
            {
                new_hora = floor(fmod((this->hora - floor(fraccionDia) - uncarryMin_a_Hrs),24));
                uncarryHrs_a_Dia = 1;
            }

        int anios_a_restar = floor(d/360);
        int meses_a_restar = floor(fmod(d,360)/30);
        int dias_a_restar = floor (fmod(fmod(d,360),30));

        int new_dia = 0;
        int uncarryDia_a_Mes = 0;
        if (dias_a_restar < this->dia)
            new_dia =  this->dia -dias_a_restar - uncarryHrs_a_Dia;
        else
            {
            new_dia = floor(fmod((this->dia -dias_a_restar - uncarryHrs_a_Dia),30));
            uncarryDia_a_Mes = 1;
            }

        int new_mes = 0;
        int uncarryMes_a_Anio = 0;
        if (meses_a_restar < this->mes)
            new_mes =  this->mes -meses_a_restar - uncarryDia_a_Mes;
        else
            {
            new_mes = floor(fmod((this->mes -meses_a_restar - uncarryDia_a_Mes),12));
            uncarryMes_a_Anio = 1;
            }

        int new_anio =  this->anio - anios_a_restar - uncarryMes_a_Anio;

        if (new_dia == 0)
            new_dia = 30;

        if (new_mes == 0)
            new_mes = 12;

        DateTime res(new_anio, new_mes, new_dia, new_hora, new_min);
        return res;
    };

double DateTime :: operator- (DateTime& dt) //Resta dos fechas, devuelve la cantidad de dias entre ellas
    {
        double dif = 0;
        DateTime arg1(this->anio, this->mes, this->dia, this->hora, this->min);
        if (arg1 != dt)
            {
                if (arg1 > dt)
                    {
                        int carryMH = 0;
                        if (this->min >= dt.min)
                            dif = dif + (this->min - dt.min)/1440;
                        else
                            {
                                dif = dif + (dt.min - this->min)/1440;
                                carryMH = 1;
                            }
                        int carryHD = 0;
                        if ((this->hora > dt.hora) ||(this->hora == dt.hora && carryMH == 0))
                            dif = dif + (this->hora - dt.hora - carryMH)/24;
                        else
                            {
                                dif = dif + (dt.hora - this->hora - carryMH)/24;
                                carryHD = 1;
                             }
                        
						int carryDM = 0;
						if ((this->dia > dt.dia) ||(this->dia == dt.dia && carryHD == 0))
                            dif = dif + this->dia - dt.dia - carryHD;
                        else
                            {
                                dif = dif +(dt.dia - this->dia - carryHD);
                                carryDM = 1;
                            }
                        
						int carryMA = 0;
						if ((this->mes > dt.mes) ||(this->mes == dt.mes && carryDM == 0))
                           dif = dif + (this->mes - dt.mes - carryDM)*30;   
                        else
                            {
                                dif = dif + (dt.mes - this->mes - carryDM)*30;
                                carryMA = 1;
                            }
                        
                        if (this->anio != dt.anio)
                            dif = dif + (this->anio - dt.anio - carryMA)*360;
                    }
                else
                    dif = (dt - arg1);
            }
        return dif;
    };
